#include <Precomp.h>
#include "defs.h"
#include "GameWorld.h"
#include "ChatList.h"
#include "Player.h"
#include "ClientListener.h"
#include "ServerConnection.h"
#include "AVUITextBox.h"
#include "AVUIListBox.h"
#include "AVUIListBoxItem.h"
#include "AVUIDispatcher.h"
#include "AVUIInputManager.h"
#include "AVUIItemCollection.h"

using namespace AVUI;

CChatList::CChatList(CGameWorld* pGameWorld, ListBox* pList, TextBox* pTextbox) : m_pGameWorld(pGameWorld), m_pList(pList), m_pEditbox(pTextbox), m_fReturnActivated(false)
{
    m_pEventSubscription = pGameWorld->SubscribeEvents( CGameWorld::EventSubscriber(&CChatList::OnGameWorldEvent, this));

    CChatHistory& chatHistory = pGameWorld->GetChatHistory();

    for(CChatHistory::iterator i = chatHistory.begin(); i != chatHistory.end(); ++i)
    {
        AddChat(&(*i));
    }

    m_pEditbox->add_KeyDown(KeyEventHandler::Create(this, &CChatList::OnEditKeyDown));
}

bool CChatList::ChatTextAccepted()
{
    if(m_pEditbox->get_Text()->get_Cch() > 0)
    {
        char szBuf[240];

        m_pEditbox->get_Text()->ConvertToAnsi(szBuf, sizeof(szBuf));

        mesg_cpacket packet;

        if(InitMesgPacketFromText(&packet, szBuf))
        {
            m_pGameWorld->GetListener()->GetServerConnection()->SendPacket((packet_base*)&packet);
        }
    }

    ClearAndDeactivate();

    return true;
}

bool CChatList::HandleKeyPressed(EKeyboardButtonID eKeyboardButtonID)
{
    bool isFocused = m_pEditbox->get_IsFocused();

    if(isFocused)
    {
        return true;
    }
    else
    {
        if(eKeyboardButtonID == eKeyboardButtonID_Return || eKeyboardButtonID == eKeyboardButtonID_Slash)
        {
            m_pEditbox->Focus();
            m_pEditbox->set_CaretIndex(0);

            return true;
        }
    }

    return false;
}


void CChatList::ClearAndDeactivate()
{
    m_pEditbox->ClearValue(AVUI::TextBox::get_TextProperty());

    Keyboard::Focus(m_pList);
}

void CChatList::OnEditKeyDown(AVUI::Object* pSender, AVUI::KeyEventArgs* pArgs)
{
    if(pArgs->get_Key() == Key::Enter)
    {
        ChatTextAccepted();
        pArgs->set_IsHandled(true);
    }
    else if(pArgs->get_Key() == Key::Escape)
    {
        ClearAndDeactivate();
        pArgs->set_IsHandled(true);
    }
}

void CChatList::OnGameWorldEvent(CGameWorldEvent* pEvent) // pGameWorldEvent)
{
    if(pEvent->GetEventID() != eGameWorldEventID_Chat)
    {
        return;
    }

    AddChat( ((CChatEvent*)pEvent)->GetMessage());
}

void CChatList::AddChat(const mesg_spacket* pChat)
{
    TRefCountedPtr<ListBoxItem> pItem = object_allocate<ListBoxItem>();

    m_pList->get_Items()->Add(pItem);

    pItem->set_Content(object_allocate<String>(pChat->mesg));
    pItem->set_FontSize(m_pList->get_FontSize());
    pItem->set_FontFamily(m_pList->get_FontFamily());
    pItem->set_Foreground(m_pList->get_Foreground());
    

    unsigned int itemCount = m_pList->get_Items()->get_Count();

    while(itemCount > c_maxChatMessages)
    {
        m_pList->get_Items()->RemoveAt(0);
        itemCount--;
    }

    pItem->BringIntoView();
}

CChatList::~CChatList() 
{
    m_pGameWorld->UnsubscribeEvents(m_pEventSubscription);
}

static const char* c_rgszTellIdentifier[] = 
{
    "/t",
    "/tell",
    "/transmit",
    "/w",
    "/whisper",
};

static const int c_cTellIdentifier = sizeof(c_rgszTellIdentifier) / sizeof(c_rgszTellIdentifier[0]);

static const char* c_rgszShoutIdentifier[] = 
{
    "/s",
    "/shout",
    "/yell",
    "/all",
    "/a"
};

static const int c_cShoutIdentifier = sizeof(c_rgszShoutIdentifier) / sizeof(c_rgszShoutIdentifier[0]);

const char* CChatList::GetChatIdentifierPosition(const char** rgszIdentifiers, int cIdentifiers, const char* szText)
{
    for(int i = 0; i < cIdentifiers; i++)                
    {
        int cch = strlen(rgszIdentifiers[i]);

        if(!_strnicmp(rgszIdentifiers[i], szText, cch) && (szText[cch] == ' ' || szText[cch] == '\0'))
        {
            int offset = cch;

            if(szText[cch] == ' ')
            {
                offset += 1;
            }
            return szText + offset;
        }
    }

    return NULL;
}


void CChatList::InitMesgPacket(EMessageGroupFlags eGroup, char chWhoSentTo, const char* szText, mesg_cpacket* pPacket)
{
    pPacket->group = (char)eGroup;
    pPacket->indiv = chWhoSentTo;
    strncpy_s(pPacket->mesg, sizeof(pPacket->mesg), szText, sizeof(pPacket->mesg));
}


bool CChatList::IsShoutMessage(const char* szChat)
{
    return GetChatIdentifierPosition(c_rgszShoutIdentifier, c_cShoutIdentifier, szChat) != NULL;
}

bool CChatList::IsTellMessage(const char* szChat)
{
    return GetChatIdentifierPosition(c_rgszTellIdentifier, c_cShoutIdentifier, szChat) != NULL;
}

const char* CChatList::GetTeamIdentifierPosition(const char* szChat, ETeamID* peTeamID)
{
    *peTeamID = eTeamID_Invalid;
    for(int eTeam = 0; eTeam < eTeamID_Count; eTeam++)
    {
        const char* szTeamText = ETeamIDToString((ETeamID)eTeam);
        int cch = strlen(szTeamText);

        if(!_strnicmp(szTeamText, szChat, cch) && (szChat[cch] == ' ' || szChat[cch] == '\0'))
        {
            int offset = cch;

            if(szChat[cch] == ' ')
            {
                offset += 1;
            }

            *peTeamID = (ETeamID)eTeam;
            return szChat + offset;
        }
    }

    return NULL;
}

bool CChatList::IsTeamMessage(const char* szChat)
{
    ETeamID eTeamID;

    return GetTeamIdentifierPosition(szChat, &eTeamID) != NULL;
}

bool CChatList::HandleTeamMessage(const char* szChat, mesg_cpacket* pPacket)
{
    ETeamID eTeamID;
    const char* szActualText = GetTeamIdentifierPosition(szChat, &eTeamID);

    InitMesgPacket(eMessageGroupFlag_Team, (char) ETeamIDToETeamFlag(eTeamID), szActualText, pPacket);
    return true;
}

bool CChatList::HandlePlayerMessage(const char* szChat, mesg_cpacket* pPacket)
{
    bool fNameAssigned = false;
    CPlayerList& playerList = m_pGameWorld->GetPlayerList();

    for(CPlayerList::iterator i = playerList.begin(); i < playerList.end(); ++i)
    {
        CGamePlayer* pPlayer = *i;
        int cchIdentifier = strlen(pPlayer->GetIdentifier());

        if(!_strnicmp(szChat, pPlayer->GetIdentifier(), cchIdentifier) && (szChat[cchIdentifier] == ' ' || szChat[cchIdentifier] == '\0'))
        {
            const char* szActualMessage = szChat + cchIdentifier;

            if(szActualMessage[0] != '\0')
            {
                szActualMessage++;
            }

            // Identifiers are always good. If we match, send it.
            InitMesgPacket(eMessageGroupFlag_Indiv, 
                           (char)pPlayer->GetPlayerID(), szActualMessage, pPacket);

            return true;
        }

        int cchName = strlen(pPlayer->GetPlayerName());

        if(!_strnicmp(szChat, pPlayer->GetPlayerName(), cchName) && (szChat[cchName] == ' ' || szChat[cchName] == '\0'))
        {
            if(!fNameAssigned)
            {
                const char* szActualMessage = szChat + cchName;

                if(szActualMessage[0] != '\0')
                {
                    szActualMessage++;
                }

                InitMesgPacket(eMessageGroupFlag_Indiv, 
                               (char) pPlayer->GetPlayerID(), szActualMessage, pPacket);
                fNameAssigned = true;
            }
            else
            {
                return false;// Error case, name collision.
            }
        }
    }

    if(!fNameAssigned)
    {
        // Error case, name not found
        return false;
    }

    return true;
}


bool CChatList::HandleTellMessage(const char* szChat, mesg_cpacket* pPacket)
{
    const char* szTellText = GetChatIdentifierPosition(c_rgszTellIdentifier, c_cTellIdentifier, szChat);

    if(IsTeamMessage(szTellText))
    {
        return HandleTeamMessage(szTellText, pPacket);
    }
    else
    {
        return HandlePlayerMessage(szTellText, pPacket);
    }

}

bool CChatList::HandleShoutMessage(const char* szChat, mesg_cpacket* pPacket)
{
    const char* szShoutText = GetChatIdentifierPosition(c_rgszShoutIdentifier, c_cShoutIdentifier, szChat);

    assert(szShoutText != NULL);
    InitMesgPacket(eMessageGroupFlag_All, 0, szShoutText, pPacket);
    return true;
}

bool CChatList::InitMesgPacketFromText(mesg_cpacket* pPacket, const char* szChat)
{
    pPacket->type = (char) EClientPacketType_Message;

    if(IsTellMessage(szChat))
    {
        return HandleTellMessage(szChat, pPacket);
    }
    else if(IsShoutMessage(szChat))
    {
        return HandleShoutMessage(szChat, pPacket);
    }
    else
    {
        InitMesgPacket(eMessageGroupFlag_Team, (char) ETeamIDToETeamFlag(m_pGameWorld->GetPlayerForLocalPlayer()->GetTeamID()), szChat, pPacket);
        return true;
    }
}

